using System;
using System.ComponentModel;
using System.Windows.Forms;

namespace Mitosoft.Definitions.Controls
{
    public partial class LabelComboBox : UserControl
    {
        #region Delegates and events

        /// <summary>
        /// Method called when <see cref="LabelComboBox.ComboBoxTextChanged"/> occurs.
        /// </summary>
        public delegate void ComboBoxTextChangedDelegate();

        /// <summary>
        /// Occurs when the text of the combo box has changed,
        /// considering <see cref="WaitingTimeToReportComboBoxTextChangedInMilliseconds"/>
        /// after the last pressed key.
        /// </summary>
        public event ComboBoxTextChangedDelegate ComboBoxTextChanged;

        /// <summary>
        /// Method called when <see cref="ComboBoxSelectedValueChanged"/> occurs.
        /// </summary>
        public delegate void ComboBoxSelectedValueChangedDelegate();

        /// <summary>
        /// Occurs when the selected value inside the combo box has changed.
        /// Keep in mind that when <see cref="ComboBoxTextChanged"/> occurs,
        /// this event will be suppressed until <see cref="ComboBoxTextChanged"/>
        /// was handled.
        /// </summary>
        public event ComboBoxSelectedValueChangedDelegate ComboBoxSelectedValueChanged;

        #endregion

        #region Fields

        protected Timer _comboBoxTextChangedTimer;
        protected uint _waitingTimeToReportComboBoxTextChangedInMilliseconds = 2000;
        protected bool _suppressComboBoxSelectedValueChangedEvent;

        #endregion

        #region Constructor(s)

        /// <summary>
        /// Creates an <see cref="LabelComboBox"/> instance.
        /// </summary>
        public LabelComboBox()
        {
            InitializeComponent();

            this.ComboBox.SelectedValueChanged += this.ComboBox_SelectedValueChanged;
        }

        #endregion

        #region Properties

        /// <summary>
        /// Gets or sets whether the <see cref="ComboBoxSelectedValueChanged"/>
        /// should be suppressed.
        /// </summary>
        [Browsable(false)]
        public bool SuppressComboBoxSelectedValueChangedEvent
        {
            get { return _suppressComboBoxSelectedValueChangedEvent; }
            set { _suppressComboBoxSelectedValueChangedEvent = value; }
        }

        /// <summary>
        /// Gets or sets the time which must elapse after the time the last
        /// key was pressed inside the combo box to throw the text changed
        /// event. Default value is 500 milliseconds.
        /// </summary>
        public uint WaitingTimeToReportComboBoxTextChangedInMilliseconds
        {
            get { return _waitingTimeToReportComboBoxTextChangedInMilliseconds; }
            set
            {
                _waitingTimeToReportComboBoxTextChangedInMilliseconds = value;
                this.ComboBoxTextChangedTimer.Interval = (int) value;
            }
        }

        /// <summary>
        /// Gets the timer used to throw an event indicating
        /// that the text in the combo box was changed.
        /// Using this timer we prevent calling the event 
        /// each time after each key.
        /// </summary>
        protected Timer ComboBoxTextChangedTimer
        {
            get
            {
                if (_comboBoxTextChangedTimer == null)
                {
                    _comboBoxTextChangedTimer = new Timer();

                    _comboBoxTextChangedTimer.Interval = 
                        (int)this.WaitingTimeToReportComboBoxTextChangedInMilliseconds;

                    _comboBoxTextChangedTimer.Tick += 
                        this.ComboBoxTextChangedTimer_Tick;
                }

                return _comboBoxTextChangedTimer;
            }
        }

        #endregion

        #region Methods

        /// <summary>
        /// Occurs when the selected value of the combo box has changed.
        /// </summary>
        /// <param name="sender">Not used.</param>
        /// <param name="e">Not used.</param>
        protected void ComboBox_SelectedValueChanged(object sender, EventArgs e)
        {
            if (this.SuppressComboBoxSelectedValueChangedEvent)
                return;

            if (this.ComboBoxSelectedValueChanged == null)
                return;

            this.ComboBoxSelectedValueChanged();
        }

        /// <summary>
        /// Listens to the tick event of the <see cref="ComboBoxTextChangedTimer"/>
        /// </summary>
        /// <param name="sender">Not used.</param>
        /// <param name="e">Not used.</param>
        protected void ComboBoxTextChangedTimer_Tick(object sender, EventArgs e)
        {
            this.ComboBoxTextChangedTimer.Stop();

            this.SuppressComboBoxSelectedValueChangedEvent = true;
            string rememberComboBoxText = this.ComboBox.Text;
            this.ComboBox.Enabled = false;

            try 
            {
                if (this.ComboBoxTextChanged != null)
                    this.ComboBoxTextChanged();
            }
            finally
            {
                this.ComboBox.SelectedText = null;
                this.ComboBox.Text = rememberComboBoxText;
                this.SuppressComboBoxSelectedValueChangedEvent = false;
                this.ComboBox.Enabled = true;
                this.ComboBox.Focus();
            }
        }

        /// <summary>
        /// Listens to the event occuring when a key was pressed inside the combo box.
        /// </summary>
        /// <param name="sender">Not used.</param>
        /// <param name="e">Not used.</param>
        protected void ComboBox_KeyPress(object sender, KeyPressEventArgs e)
        {
            this.ComboBoxTextChangedTimer.Stop();
            this.ComboBoxTextChangedTimer.Start();
        }

        #endregion

    }
}
